"""
多平台安全审计执行器
支持Windows和Linux系统的安全配置审计
"""

import json
import os
import sys
import time
import importlib
from datetime import datetime
from typing import Dict, List, Any, Optional
from config.config import AUDIT_TYPES
from util import common


class MultiPlatformAuditExecutor:
    """多平台安全审计执行器"""

    def __init__(self):
        self.results = {}
        self.start_time = None
        self.end_time = None
        self.current_audit_type = None
        self.audit_rules_class = None

    def load_audit_rules(self, audit_type: str) -> bool:
        """动态加载指定平台的审计规则"""
        try:
            if audit_type not in AUDIT_TYPES:
                print(f"错误: 不支持的审计类型: {audit_type}")
                print(f"支持的审计类型: {', '.join(AUDIT_TYPES.keys())}")
                return False

            config = AUDIT_TYPES[audit_type]
            module_name = config["module"]
            class_name = config["class_name"]
            module_name = "rules." + module_name

            # 动态导入模块
            module = importlib.import_module(module_name)
            # 获取规则类
            audit_rules_class = getattr(module, class_name)

            self.current_audit_type = audit_type
            self.audit_rules_class = audit_rules_class

            print(f"✓ 成功加载 {config['description']} 规则")
            return True

        except ImportError as e:
            print(f"错误: 无法导入审计规则模块 {module_name}: {e}")
            return False
        except AttributeError as e:
            print(f"错误: 审计规则类 {class_name} 不存在: {e}")
            return False
        except Exception as e:
            print(f"错误: 加载审计规则失败: {e}")
            return False

    def execute_single_rule(self, rule_func: callable) -> Dict[str, Any]:
        """执行单个审计规则"""
        try:
            result = rule_func()
            return result
        except Exception as e:
            rule_name = rule_func.__name__ if hasattr(rule_func, '__name__') else 'unknown'
            return {
                "rule_name": rule_name,
                "status": "执行失败",
                "error": str(e),
                "recommendation": "检查命令执行环境"
            }

    def execute_all_rules(self, audit_type: str, specific_rules: List[str] = None) -> Dict[str, Any]:
        """执行指定平台的所有审计规则"""
        print("=" * 60)
        print(f"{AUDIT_TYPES[audit_type]['description']}开始执行")
        print("=" * 60)

        # 加载审计规则
        if not self.load_audit_rules(audit_type):
            return {"error": f"无法加载 {audit_type} 审计规则"}

        self.start_time = datetime.now()

        # 确定要执行的规则列表
        if specific_rules:
            rules_to_execute = specific_rules
        else:
            rules_to_execute = AUDIT_TYPES[audit_type]["rules"]

        print(f"即将执行 {len(rules_to_execute)} 个审计规则...")

        for i, rule_name in enumerate(rules_to_execute, 1):
            print(f"\n[{i}/{len(rules_to_execute)}] 执行规则检查...")

            try:
                # 动态获取规则函数
                rule_func = getattr(self.audit_rules_class, rule_name)
                result = self.execute_single_rule(rule_func)

                # 使用规则名称作为键
                self.results[rule_name] = result

                # 显示简要结果
                status_icon = "✓" if result.get('status') in ['正常', '通过', 'OK'] else "✗"
                rule_display_name = result.get('rule_name', rule_name.replace('check_', ''))
                print(f"   {status_icon} {rule_display_name}: {result.get('status', '未知')}")

            except AttributeError:
                error_result = {
                    "rule_name": rule_name,
                    "status": "规则不存在",
                    "error": f"规则函数 {rule_name} 不存在",
                    "recommendation": "检查规则名称是否正确"
                }
                self.results[rule_name] = error_result
                print(f"   ✗ {rule_name}: 规则不存在")
            except Exception as e:
                error_result = {
                    "rule_name": rule_name,
                    "status": "执行异常",
                    "error": str(e),
                    "recommendation": "检查规则实现"
                }
                self.results[rule_name] = error_result
                print(f"   ✗ {rule_name}: 执行异常 - {str(e)}")

            # 添加短暂延迟，避免系统资源冲突
            time.sleep(0.5)

        self.end_time = datetime.now()
        return self.results

    def generate_summary_report(self) -> Dict[str, Any]:
        """生成摘要报告"""
        if not self.results:
            return {"error": "请先执行审计规则"}

        total_rules = len(self.results)
        passed_rules = sum(1 for result in self.results.values()
                           if result.get('status') in ['正常', '通过', 'OK'])
        failed_rules = total_rules - passed_rules

        # 计算风险等级
        if failed_rules == 0:
            risk_level = "低风险"
        elif failed_rules <= total_rules * 0.3:  # 失败规则少于30%
            risk_level = "中风险"
        else:
            risk_level = "高风险"

        return {
            "audit_summary": {
                "audit_type": self.current_audit_type,
                "audit_description": AUDIT_TYPES.get(self.current_audit_type, {}).get('description', '未知'),
                "total_rules": total_rules,
                "passed_rules": passed_rules,
                "failed_rules": failed_rules,
                "success_rate": f"{(passed_rules / total_rules) * 100:.1f}%" if total_rules > 0 else "0%",
                "risk_level": risk_level,
                "start_time": self.start_time.isoformat() if self.start_time else None,
                "end_time": self.end_time.isoformat() if self.end_time else None,
                "duration_seconds": (
                        self.end_time - self.start_time).total_seconds() if self.start_time and self.end_time else None
            },
            "detailed_results": self.results
        }

    def save_report_to_file(self, filename: str = None) -> str:
        """保存报告到文件"""
        if not filename:
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            filename = f"{self.current_audit_type}_security_audit_{timestamp}.json"

        # 将报告输出到output目录中
        proj_root_dir = sys.path[4]
        output_dir = os.path.join(proj_root_dir, "output")
        if not os.path.exists(output_dir):
            # 如果不存在，创建目录
            os.makedirs(output_dir)
        filename = os.path.join(output_dir, filename)  # 将带有绝对路径的filename创建出来

        report = self.generate_summary_report()

        with open(filename, 'w', encoding='utf-8') as f:
            json.dump(report, f, ensure_ascii=False, indent=2)

        return filename

    def generate_html_report(self, filename: str = None) -> str:
        """生成HTML格式的报告"""
        if not self.results:
            return "没有可用的审计结果"

        summary = self.generate_summary_report()['audit_summary']
        detailed_results = self.results

        if not filename:
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            filename = f"{self.current_audit_type}_security_audit_{timestamp}.html"

        # 将报告输出到output目录中
        proj_root_dir = sys.path[4]
        output_dir = os.path.join(proj_root_dir, "output")
        if not os.path.exists(output_dir):
            # 如果不存在，创建目录
            os.makedirs(output_dir)
        filename = os.path.join(output_dir, filename)  # 将带有绝对路径的filename创建出来

        # HTML模板
        html_template = """
        <!DOCTYPE html>
        <html lang="zh-CN">
        <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>安全审计报告 - {audit_type}</title>
            <style>
                * {{
                    margin: 0;
                    padding: 0;
                    box-sizing: border-box;
                }}
                body {{
                    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
                    line-height: 1.6;
                    color: #333;
                    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
                    min-height: 100vh;
                    padding: 20px;
                }}
                .container {{
                    max-width: 1200px;
                    margin: 0 auto;
                    background: white;
                    border-radius: 15px;
                    box-shadow: 0 20px 40px rgba(0,0,0,0.1);
                    overflow: hidden;
                }}
                .header {{
                    background: linear-gradient(135deg, #2c3e50, #34495e);
                    color: white;
                    padding: 30px;
                    text-align: center;
                }}
                .header h1 {{
                    font-size: 2.5em;
                    margin-bottom: 10px;
                    font-weight: 300;
                }}
                .header .subtitle {{
                    font-size: 1.2em;
                    opacity: 0.9;
                }}
                .summary {{
                    background: #f8f9fa;
                    padding: 30px;
                    border-bottom: 1px solid #e9ecef;
                }}
                .summary-grid {{
                    display: grid;
                    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
                    gap: 20px;
                    margin-top: 20px;
                }}
                .summary-item {{
                    background: white;
                    padding: 20px;
                    border-radius: 10px;
                    text-align: center;
                    box-shadow: 0 5px 15px rgba(0,0,0,0.1);
                }}
                .summary-item .value {{
                    font-size: 2em;
                    font-weight: bold;
                    margin-bottom: 5px;
                }}
                .summary-item .label {{
                    color: #6c757d;
                    font-size: 0.9em;
                }}
                .risk-high {{
                    color: #dc3545;
                }}
                .risk-medium {{
                    color: #ffc107;
                }}
                .risk-low {{
                    color: #28a745;
                }}
                .details {{
                    padding: 30px;
                }}
                .rule-card {{
                    background: white;
                    border: 1px solid #e9ecef;
                    border-radius: 10px;
                    margin-bottom: 20px;
                    overflow: hidden;
                    box-shadow: 0 5px 15px rgba(0,0,0,0.1);
                    transition: transform 0.3s ease;
                }}
                .rule-card:hover {{
                    transform: translateY(-5px);
                }}
                .rule-header {{
                    padding: 20px;
                    border-bottom: 1px solid #e9ecef;
                    display: flex;
                    justify-content: between;
                    align-items: center;
                }}
                .rule-status {{
                    padding: 5px 15px;
                    border-radius: 20px;
                    font-weight: bold;
                    font-size: 0.9em;
                }}
                .status-normal {{
                    background: #d4edda;
                    color: #155724;
                }}
                .status-abnormal {{
                    background: #f8d7da;
                    color: #721c24;
                }}
                .status-failed {{
                    background: #fff3cd;
                    color: #856404;
                }}
                .rule-content {{
                    padding: 20px;
                }}
                .status-details {{
                    background: #f8f9fa;
                    padding: 15px;
                    border-radius: 5px;
                    margin: 15px 0;
                }}
                .recommendation {{
                    background: #e7f3ff;
                    padding: 15px;
                    border-radius: 5px;
                    border-left: 4px solid #007bff;
                    margin-top: 15px;
                }}
                .footer {{
                    background: #2c3e50;
                    color: white;
                    text-align: center;
                    padding: 20px;
                    margin-top: 30px;
                }}
                .timestamp {{
                    color: #6c757d;
                    font-size: 0.9em;
                    margin-top: 10px;
                }}
                .icon {{
                    font-size: 1.2em;
                    margin-right: 5px;
                }}
            </style>
        </head>
        <body>
            <div class="container">
                <div class="header">
                    <h1>🔒 安全审计报告</h1>
                    <div class="subtitle">{audit_description} - {audit_type}</div>
                </div>

                <div class="summary">
                    <h2>📊 审计摘要</h2>
                    <div class="summary-grid">
                        <div class="summary-item">
                            <div class="value">{total_rules}</div>
                            <div class="label">检查规则</div>
                        </div>
                        <div class="summary-item">
                            <div class="value" style="color: #28a745;">{passed_rules}</div>
                            <div class="label">通过规则</div>
                        </div>
                        <div class="summary-item">
                            <div class="value" style="color: #dc3545;">{failed_rules}</div>
                            <div class="label">异常规则</div>
                        </div>
                        <div class="summary-item">
                            <div class="value">{success_rate}</div>
                            <div class="label">成功率</div>
                        </div>
                        <div class="summary-item">
                            <div class="value risk-{risk_level_class}">{risk_level}</div>
                            <div class="label">风险等级</div>
                        </div>
                        <div class="summary-item">
                            <div class="value">{duration_seconds:.1f}s</div>
                            <div class="label">执行时间</div>
                        </div>
                    </div>
                    <div class="timestamp">
                        执行时间: {start_time} | 持续时间: {duration_seconds:.1f}秒
                    </div>
                </div>

                <div class="details">
                    <h2>📋 详细配置状态</h2>
                    {rule_cards}
                </div>

                <div class="footer">
                    <p>💡 重要提示: 由于自动化检测可能出现误判，建议将报告结果交给AI或人工分析验证后再进行处理！</p>
                    <p class="timestamp">报告生成时间: {current_time}</p>
                </div>
            </div>
        </body>
        </html>
        """

        # 生成规则卡片HTML
        rule_cards_html = ""
        for rule_name, result in detailed_results.items():
            # 确定状态类
            status_class = "status-normal"
            if result.get('status') in ['异常', '执行失败', '规则不存在']:
                status_class = "status-abnormal"
            elif result.get('status') in ['检查失败', '执行异常']:
                status_class = "status-failed"

            # 生成状态详情HTML
            status_details_html = ""
            if 'status_details' in result and result['status_details']:
                status_details_html = '<div class="status-details">'
                for detail in result['status_details']:
                    status_details_html += f'<div>{detail}</div>'
                status_details_html += '</div>'

            # 生成配置状态HTML
            config_html = ""
            if 'config_status' in result and result['config_status']:
                config_html = '<div><strong>配置状态:</strong><ul style="margin-left: 20px; margin-top: 10px;">'
                for key, value in result['config_status'].items():
                    config_html += f'<li>{key}: {value}</li>'
                config_html += '</ul></div>'

            # 生成错误信息HTML
            error_html = ""
            if 'error' in result and result['error']:
                error_html = f'<div style="color: #dc3545; margin: 10px 0;"><strong>错误信息:</strong> {result["error"]}</div>'

            rule_cards_html += f"""
            <div class="rule-card">
                <div class="rule-header">
                    <h3>{result.get('rule_name', rule_name.replace('check_', ''))}</h3>
                    <div class="rule-status {status_class}">
                        {result.get('status', '未知')}
                    </div>
                </div>
                <div class="rule-content">
                    {status_details_html}
                    {config_html}
                    {error_html}
                    <div class="recommendation">
                        <strong>💡 建议:</strong> {result.get('recommendation', '无')}
                    </div>
                </div>
            </div>
            """

        # 风险等级CSS类
        risk_level_class = "low" if summary['risk_level'] == "低风险" else "medium" if summary[
                                                                                           'risk_level'] == "中风险" else "high"

        # 填充模板
        html_content = html_template.format(
            audit_type=summary['audit_type'],
            audit_description=summary['audit_description'],
            total_rules=summary['total_rules'],
            passed_rules=summary['passed_rules'],
            failed_rules=summary['failed_rules'],
            success_rate=summary['success_rate'],
            risk_level=summary['risk_level'],
            risk_level_class=risk_level_class,
            start_time=summary['start_time'],
            duration_seconds=summary['duration_seconds'],
            rule_cards=rule_cards_html,
            current_time=datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        )

        # 保存HTML文件
        with open(filename, 'w', encoding='utf-8') as f:
            f.write(html_content)

        return filename

    def print_console_report(self):
        """在控制台打印详细配置报告"""
        if not self.results:
            print("没有可用的审计结果")
            return

        summary = self.generate_summary_report()['audit_summary']

        print("\n" + "=" * 80)
        print(f"{summary['audit_description']} - 详细审计报告")
        print("=" * 80)
        print(f"审计平台: {summary['audit_type']}")
        print(f"执行时间: {summary['start_time']}")
        print(f"持续时间: {summary['duration_seconds']:.1f} 秒")
        print(f"检查规则: {summary['total_rules']} 个")
        print(f"通过规则: {summary['passed_rules']} 个")
        print(f"异常规则: {summary['failed_rules']} 个")
        print(f"成功率: {summary['success_rate']}")
        print(f"风险等级: {summary['risk_level']}")
        print("\n详细配置状态:")
        print("=" * 80)

        for rule_name, result in self.results.items():
            status_icon = "✓" if result.get('status') in ['正常', '通过', 'OK'] else "✗"
            display_name = result.get('rule_name', rule_name.replace('check_', ''))
            print(f"\n{status_icon} {display_name}: {result.get('status')}")
            print("-" * 60)

            # 打印详细配置状态
            if 'status_details' in result and result['status_details']:
                for detail in result['status_details']:
                    # 处理长行换行
                    if len(detail) > 100:
                        words = detail.split()
                        line = ""
                        for word in words:
                            if len(line + word) < 80:
                                line += word + " "
                            else:
                                print(f"  {line}")
                                line = word + " "
                        if line:
                            print(f"  {line}")
                    else:
                        print(f"  {detail}")
            else:
                config = result.get('config_status', {})
                for key, value in config.items():
                    print(f"  {key}: {value}")

            # 显示建议
            if result.get('recommendation'):
                print(f"  建议: {result.get('recommendation')}")

            print()  # 空行分隔不同检查项

    def list_supported_platforms(self):
        """列出支持的审计平台"""
        print("支持的审计平台:")
        print("-" * 40)
        for platform, config in AUDIT_TYPES.items():
            print(f"  {platform:8} - {config['description']}")


def main():
    """主执行函数"""
    # 创建执行器
    executor = MultiPlatformAuditExecutor()

    try:
        # 显示所有支持的平台（分类显示）
        print("=" * 60)
        print("           多平台安全审计执行器")
        print("=" * 60)

        # 分类显示审计选项
        categories = {
            "操作系统审计": ["windows", "linux"],
            "Web服务器审计": ["nginx", "apache", "tomcat"],
            "数据库审计": ["mysql", "redis"]

        }

        print("\n请选择审计类型:")
        print("-" * 40)

        option_number = 1
        option_mapping = {}  # 映射数字选项到平台名称

        for category, platforms in categories.items():
            print(f"\n{category}:")
            for platform in platforms:
                config = AUDIT_TYPES[platform]
                status_icon = "✅" if config.get("status") != "待实现" else "⏳"
                print(f"  {option_number:2d}. {platform:8} - {config['description']} {status_icon}")
                option_mapping[str(option_number)] = platform
                option_mapping[platform] = platform  # 同时支持名称输入
                option_number += 1

        print("\n" + "-" * 40)

        # 获取用户输入（支持数字或名称）
        user_input = input("请输入审计平台编号或名称: ").strip().lower()

        # 确定审计类型
        if user_input in option_mapping:
            audit_type = option_mapping[user_input]
        elif user_input in AUDIT_TYPES:
            audit_type = user_input
        else:
            print(f"错误: 不支持的平台 '{user_input}'")
            print("提示: 可以输入编号(如1)或平台名称(如linux)")
            return

        config = AUDIT_TYPES[audit_type]

        # 检查是否已实现
        if config.get("status") == "待实现":
            print(f"\n⚠️  注意: {config['description']} 功能尚未完全实现")
            print("当前只能进行基础框架测试")
            continue_exec = input("是否继续? (y/N): ").strip().lower()
            if continue_exec != 'y':
                print("已取消执行")
                return

        # 询问是否执行特定规则
        specific_rules = None
        if config.get("status") != "待实现":  # 只有已实现的才显示规则选择
            use_specific = input("是否执行特定规则? (y/N): ").strip().lower()
            if use_specific == 'y':
                available_rules = config["rules"]
                print("\n可用规则:")
                for i, rule in enumerate(available_rules, 1):
                    print(f"  {i:2d}. {rule}")

                rule_input = input("请输入规则名称或序号 (多个用逗号分隔): ").strip()
                if rule_input:
                    specific_rules = []
                    for item in rule_input.split(','):
                        item = item.strip()
                        if item.isdigit():
                            index = int(item) - 1
                            if 0 <= index < len(available_rules):
                                specific_rules.append(available_rules[index])
                        else:
                            specific_rules.append(item)

        # 执行审计
        print(f"\n开始执行 {config['description']}...")
        results = executor.execute_all_rules(audit_type, specific_rules)

        # 生成并显示控制台报告
        executor.print_console_report()

        # 保存JSON报告
        json_report_file = executor.save_report_to_file()
        print(f"\n详细JSON报告已保存到: {json_report_file}")

        # 生成HTML报告
        html_report_file = executor.generate_html_report()
        print(f"HTML可视化报告已保存到: {html_report_file}")

        # 添加重要提示
        print("\n" + "⚠️" * 40)
        print("💡 重要提示: 由于自动化检测可能出现误判，建议将报告结果交给AI或人工分析验证后再进行处理！")
        print("⚠️" * 40)

        # 询问是否打开HTML报告
        open_html = input("\n是否立即打开HTML报告? (y/N): ").strip().lower()
        if open_html == 'y':
            import webbrowser
            import os
            webbrowser.open('file://' + os.path.abspath(html_report_file))
            print("正在浏览器中打开HTML报告...")

    except KeyboardInterrupt:
        print("\n用户中断了审计过程")
    except Exception as e:
        print(f"审计执行过程中发生错误: {e}")

    input("\n按任意键退出...")


if __name__ == "__main__":
    main()
